package middleware

import (
	"github.com/gin-gonic/gin"
	"go.uber.org/zap"
	"go.uber.org/zap/zapcore"
	"gopkg.in/natefinch/lumberjack.v2"
	"time"
)

// 声明一个日志记录器实例
var sugarLogger *zap.SugaredLogger

// 中间件函数
func Logger() gin.HandlerFunc {
	InitLogger()
	return func(c *gin.Context) {
		//在请求处理前，记录一条Debug级别的日志，包含请求的URL路径
		sugarLogger.Debugf("Trying to access logger %s", c.Request.URL.Path)
		c.Next()                        //执行后续中间件和路由处理函数
		statusCode := c.Writer.Status() //获取响应状态码
		if len(c.Errors) > 0 {
			sugarLogger.Error(c.Errors.String())
		}
		if statusCode >= 400 {
			sugarLogger.Warnf("客户端警告: %d", statusCode)
		} else if statusCode >= 500 {
			sugarLogger.Errorf("服务器错误: %d", statusCode)
		} else {
			sugarLogger.Infof("请求正常: %d", statusCode)
		}

	}

}

// 初始化日志器
func InitLogger() {
	encoder := getEncoder()       //获取编码器（定义日志格式）
	writeSyncer := getLogWriter() //获取输出目标（日志写到哪里）
	//创建一个核心日志处理器，相当于日志处理引擎
	core := zapcore.NewCore(encoder, writeSyncer, zapcore.DebugLevel)
	//基于core创建Logger实例
	// zap.AddCaller()是可选配置，作用是让日志种自动包含调用日志的代码位置信息（即文件名和行号）
	logger := zap.New(core, zap.AddCaller())
	//从一个标准的zap.Logger实例创建并获取一个zap.SugaredLogger实例
	sugarLogger = logger.Sugar()
}

// 使用JSON格式的编码器，并采用开发环境的默认配置，包括日志级别和时间戳等字段
func getEncoder() zapcore.Encoder {
	config := zap.NewProductionEncoderConfig()
	//修改时间格式
	config.EncodeTime = func(t time.Time, enc zapcore.PrimitiveArrayEncoder) {
		enc.AppendString(t.Format("2006-01-02 15:04:05"))
	}
	//将日志级别显示为中文
	config.EncodeLevel = func(level zapcore.Level, enc zapcore.PrimitiveArrayEncoder) {
		switch level {
		case zapcore.DebugLevel:
			enc.AppendString("调试")
		case zapcore.InfoLevel:
			enc.AppendString("信息")
		case zapcore.WarnLevel:
			enc.AppendString("警告")
		case zapcore.ErrorLevel:
			enc.AppendString("错误")
		case zapcore.DPanicLevel:
			enc.AppendString("严重错误")
		case zapcore.PanicLevel:
			enc.AppendString("恐慌")
		case zapcore.FatalLevel:
			enc.AppendString("致命")
		default:
			enc.AppendString(level.String())
		}
	}
	// 修改字段名为中文
	config.MessageKey = "消息"
	config.LevelKey = "级别"
	config.TimeKey = "时间"
	config.CallerKey = "位置"
	config.StacktraceKey = "堆栈"

	return zapcore.NewJSONEncoder(config)
}

// 日志输出配置
func getLogWriter() zapcore.WriteSyncer {
	//创建日志文件轮转管理器实例
	//使用lumberjack库实现日志轮转
	lumberjackLogger := &lumberjack.Logger{
		Filename:   "./logs/gin.log", //文件路径，不存在会自动创建
		MaxSize:    200,              //单个文件的最大大小（MB）
		MaxBackups: 10,               //保留的最大备份文件数
		MaxAge:     30,               //日志文件的最大保存天数
		Compress:   true,             //是否压缩备份文件
	}
	return zapcore.AddSync(lumberjackLogger)
}
